// --------------------------------------------------------------------------------------------------------------------
// <copyright file="EnumExtensions.cs" company="CSLib.Net Team">
//   Copyright (c) Roman "4ux-nbIx" Novitsky 2010
// </copyright>
// <summary>
//   Defines Enum class extension methods.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace CSLib.Net.Extensions
{
  #region Namespace Imports

  using System;
  using System.Diagnostics.CodeAnalysis;
  using System.Globalization;

  using Annotations;

  using Diagnostics;

  #endregion


  /// <summary>
  /// Defines <see cref="Enum"/> class extension methods.
  /// </summary>
  [UsedImplicitly(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.WithMembers)]
  public static class EnumExtensions
  {
    #region Public Methods

    /// <summary>
    /// Determines whether the specified value has flag.
    /// </summary>
    /// <param name="value">The current flags value.</param>
    /// <param name="valueToCheck">The flag value to check.</param>
    /// <returns>
    /// <c>true</c> if the specified value has flag; otherwise, <c>false</c>.
    /// </returns>
    /// <exception cref="ArgumentException">The specified enum value types do not match.</exception>
    [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flag", Justification = "As designed.")]
    public static bool HasFlag([NotNull] this Enum value, [NotNull] Enum valueToCheck)
    {
      Assert.ArgumentNotNull(value, "value");
      Assert.ArgumentNotNull(valueToCheck, "valueToCheck");

#if WINDOWS_PHONE
      if (value.GetType() != valueToCheck.GetType())
      {
          throw new ArgumentException("The specified enum value types do not match.");
      }

      ulong num = ToUInt64(valueToCheck);

      return (ToUInt64(value) & num) == num;
      
#else
      return value.HasFlag(valueToCheck);
#endif
    }

#if WINDOWS_PHONE
    /// <summary>
    /// Converts the specified enum value to UInt64.
    /// </summary>
    /// <param name="value">The value to convert.</param>
    /// <returns>Returns a converted value.</returns>
    /// <exception cref="InvalidOperationException">Unknown enum type.</exception>
    internal static ulong ToUInt64([NotNull] this Enum value)
    {
      Assert.ArgumentNotNull(value, "value");

      switch (Convert.GetTypeCode(value))
      {
        case TypeCode.SByte:
        case TypeCode.Int16:
        case TypeCode.Int32:
        case TypeCode.Int64:
          return (ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture);

        case TypeCode.Byte:
        case TypeCode.UInt16:
        case TypeCode.UInt32:
        case TypeCode.UInt64:
          return Convert.ToUInt64(value, CultureInfo.InvariantCulture);
      }

      throw new InvalidOperationException("Unknown enum type.");
    }
#endif


    #endregion
  }
}